package itsm.isperp.framework.core.context;

import itsm.isperp.framework.cache.ApplicationCache;
import itsm.isperp.framework.data.dialect.DialectFactory;
import itsm.isperp.framework.data.domain.DataResponse;
import itsm.isperp.framework.domain.IMenu;
import itsm.isperp.framework.domain.IUser;
import itsm.isperp.framework.domain.TempOperator;
import itsm.isperp.framework.message.impl.EmailReader;
import itsm.isperp.framework.message.impl.EmailSender;
import itsm.isperp.framework.security.domain.RoleResource;
import itsm.isperp.framework.security.domain.ServerInfo;
import itsm.isperp.framework.security.license.LicenseUtils;
import itsm.isperp.framework.service.IMenuService;
import itsm.isperp.framework.utils.JsonBuilder;
import itsm.isperp.framework.utils.ServerUtils;
import itsm.isperp.framework.web.request.JqGridRequest;
import itsm.isperp.module.entity.app.AppConfig;
import itsm.isperp.module.entity.app.AppSequence;
import itsm.isperp.module.service.app.AppConfigService;
import itsm.isperp.module.service.app.AppSequenceService;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import me.chanjar.weixin.cp.api.WxCpInMemoryConfigStorage;
import me.chanjar.weixin.cp.api.WxCpService;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.jdbc.support.DatabaseType;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.XmlWebApplicationContext;

import com.fr.stable.StringUtils;

/**
 * 上下文环境保存器.<br/>
 * 此类用于:<br/>
 * <ul>
 * <li>获取或设置当前用户会话信息{@link FrameworkContext}对象的辅助类.</li>
 * <li>获取当前线程的Request、Response以及特殊的模块ID（mid）</li>
 * <li>由于其被配置到Spring中(configs.xml),且实现了{@link ApplicationContextAware}
 * 接口,因此其内部的唯一实例保存了ApplicationContext,可以据此感知Spring.详情请参见
 * {@link #getSpringBeanFactory}和{@link #getSpringBean}两个函数.</li>
 * </ul>
 * 
 */
public class ContextHolder implements ApplicationContextAware {

	private String uploadDir;

	private String uploadPath;

	public void setUploadPath(String uploadPath) {
		this.uploadPath = uploadPath;
	}

	public void setUploadDir(String uploadDir) {
		this.uploadDir = uploadDir;
	}

	private String jdbcDialect;

	/**
	 * 系统临时目录地址
	 */
	private static String SYSTEM_TEMP_DIR;

	/**
	 * 
	 */
	private static String UPLOADFILE_DIR;

	/**
	 * 
	 */
	private static String UPLOADFILE_STORE;

	private static String APPLICATION_PATH;

	private static String CLASSES_PATH;

	public static ServerInfo SERVER_INFO;

	public static final String CONFIG_RESOURCES_MAP = "itsm.configResoucesMap";

	public static final String MENU_RESOURCES_MAP = "itsm.menuResoucesMap";

	public static final String SEQ_RULE_MAP = "itsm.seqRuleMap";

	public static final String uploadFileMaxSizeKey = "fileSize";

	public static final String uploadFileTypesKey = "fileFormat";

	public static String LIC_DATA;
	public static String LIC_SING;

	public String getJdbcDialect() {
		return jdbcDialect;
	}

	public void setJdbcDialect(String jdbcDialect) {
		this.jdbcDialect = jdbcDialect;
	}

	public static String getTempFileStorePath() {
		return SYSTEM_TEMP_DIR;
	}

	public static String getUploadFileMaxSize() {
		return getConfigByKey(uploadFileMaxSizeKey);
	}

	public static String getUploadFileTypes() {
		return getConfigByKey(uploadFileTypesKey);
	}

	/**
	 * 上传文件保存的绝对位置
	 * 
	 * @return
	 */
	public static String getUploadFileStorePath() {
		return UPLOADFILE_DIR;
	}

	/**
	 * 上传文件保存的相对位置
	 */
	public static String getUploadFileStore() {
		return UPLOADFILE_STORE;
	}

	/**
	 * 站点/应用所在的路径
	 * 
	 * @return 路径
	 */
	public static String getApplicationPath() {
		return APPLICATION_PATH;
	}

	public static String setApplicationPath(String path) {
		return APPLICATION_PATH = path;
	}

	public static String getApplicationClassPath() {
		return CLASSES_PATH;
	}

	/**
	 * 保存HttpServletResponse的线程局部变量.
	 */
	private static final ThreadLocal<HttpServletResponse> responseThreadLocal = new ThreadLocal<HttpServletResponse>();
	/**
	 * 保存HttpServletRequest的线程局部变量.
	 */
	private static final ThreadLocal<HttpServletRequest> requestThreadLocal = new ThreadLocal<HttpServletRequest>();

	private static ApplicationContext webApplicationContext = null;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.context.ApplicationContextAware#setApplicationContext
	 * (org.springframework.context.ApplicationContext)
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {

		ContextHolder.webApplicationContext = applicationContext;
		if (applicationContext instanceof XmlWebApplicationContext) {

			APPLICATION_PATH = ((XmlWebApplicationContext) webApplicationContext)
					.getServletContext().getRealPath("/");

			CLASSES_PATH = APPLICATION_PATH + "/WEB-INF/classes/";

		} else {
			APPLICATION_PATH = System.getProperty("java.io.tmpdir");
		}

		UPLOADFILE_STORE = this.uploadDir.indexOf("file:") == 0 ? this.uploadDir
				.substring(6) : this.uploadDir;

		// 仅服务于web环境.
		if (UPLOADFILE_STORE.indexOf("//") == 0) {

			UPLOADFILE_DIR = UPLOADFILE_STORE;
			APPLICATION_PATH = "//";
		} else {
			UPLOADFILE_DIR = APPLICATION_PATH + "/" + UPLOADFILE_STORE;
		}

		SYSTEM_TEMP_DIR = System.getProperty("java.io.tmpdir") + "/temp/";

		if ("mysql".equals(this.jdbcDialect)) {
			DialectFactory.setDatabaseType(DatabaseType.MYSQL);
		} else if ("oracle".equals(this.jdbcDialect)) {
			DialectFactory.setDatabaseType(DatabaseType.ORACLE);
		}

		File f = new File(SYSTEM_TEMP_DIR);

		if (!f.exists()) {
			f.mkdirs();
		}

		f = new File(UPLOADFILE_DIR);
		if (!f.exists()) {
			f.mkdirs();
		}

	}

	public static final void rebuildConfig() {

		if (webApplicationContext instanceof XmlWebApplicationContext) {

			rebuildConfigs();

			rebuildSeqRlue();
			
			
			try {
				String[] datas = LicenseUtils.initLicense();

				LIC_DATA = datas[0];
				LIC_SING = datas[1];
				if (LicenseUtils.verify()) {

					SERVER_INFO = JsonBuilder.buildNormalBinder().fromJson(
							LicenseUtils.getData(), ServerInfo.class);

					if (!"*".equals(SERVER_INFO.getMac())
							&& !ServerUtils.getMac().equals(
									SERVER_INFO.getMac())) {
						SERVER_INFO = null;
					}
				}

			} catch (Exception e) {
				// System.out.println(e);
				// LIC_DATA =
				// "124c68351a0daf0b00dddc396e15d7a92bf15082dddb6dc127b308a786ce52bb1798930249aeade874d2cdb0ab1f4f8cceb36e946f81ea7232a9cb4eb0fff81d2234e5bc34eef8c8959059f0eb7a6df99a477d9b45b5325afff21fa71af82a2ef9b51ccc9fbaaa5089902a6a35ea4736be88c48b7f464b6995cb28c51edcd642454286313e1e11aac44e72c2db086edd622d2ef4cd377557334c6aee6157198ee4b918b61c5eac3efc8d167465515d5490f40b803f1812cb8e860bb57e921109fe192d87d5b0fce77ed2a66a170efcc1e1125dc4d80ad75b9697151eae8c5ce8b1e39304420324bcbeddb100092779c7b7e16296d70bf2d1e5796139531cecd8";
				// LIC_SING =
				// "PusHr8IIt5yevVnvDOVF/7Fu7Pl3XGRUQlSuLuUEqqPBQrH+7a9KDUDNh16qj4vC4H2zJKjDzzXA8R/NWDJikzdwiiJSysQbmxmjuB7O8GeECpu/3xEAi9LFtKrrw1bcZ1aV5Rlwysl9sMjU80TmmDYzjlchAL5NHm7VJ7RqEXyZ1t25/72r+PhpS+jHV8/7xBw3O8amfZ81/E1PDmHBfmGTDwAbpsXafz/RuoSN4ORNBkvETN+92eG3HB/6jRrmMglX0EP+xUKLimWfZRNHiJpyRNrAEblWwqPFGOr25oUbRIe+VpjSSzyP5SWOI826qX0o8R9uMylEaf0ukFD7kQ==";
			}

		}
	}

	public static final boolean isWxCpEnable() {
		return "1".equals(getConfig().get("wechatCpEnable"));

	}public static final boolean isWxMpEnable() {
		return "1".equals(getConfig().get("wechatMpEnable"));

	}

	public static final WxCpInMemoryConfigStorage getWxCpConfig() {
		Map<String, String> maps = getConfig();
		String wechatCpEnable = maps.get("wechatCpEnable");

		if ("1".equals(wechatCpEnable)) {
			WxCpInMemoryConfigStorage config = new WxCpInMemoryConfigStorage();

			config.setAgentId(maps.get("wechatCpAgentId"));
			config.setCorpId(maps.get("wechatCpAppId"));
			config.setToken(maps.get("wechatCpToken"));
			config.setAesKey(maps.get("wechatCpAesKey"));
			config.setCorpSecret(maps.get("wechatCpAppSecret"));

			config.setTmpDirFile(new File(SYSTEM_TEMP_DIR));
			return config;
		}

		return null;

	}

	public static final WxMpInMemoryConfigStorage getWxMpConfig() {

		Map<String, String> maps = getConfig();
		String wechatMpEnable = maps.get("wechatMpEnable");

		if ("1".equals(wechatMpEnable)) {

			WxMpInMemoryConfigStorage config = new WxMpInMemoryConfigStorage();

			config.setAppId(maps.get("wechatMpAppId"));
			config.setSecret(maps.get("wechatMpAppSecret"));
			config.setToken(maps.get("wechatMpToken"));
			config.setAesKey(maps.get("wechatMpAesKey"));
			config.setTmpDirFile(new File(SYSTEM_TEMP_DIR));
			return config;
		}
		return null;
	}

	/**
	 * 是否运行模式
	 * 
	 * @return
	 */
	public static final boolean isRunMode() {
		// TODO
		return "1".equals(getConfigByKey("systemRunMode"));
	}

	@SuppressWarnings("unchecked")
	public static final Map<String, String> getConfig() {

		return ((Map<String, String>) getApplicationCache().getCacheObject(
				CONFIG_RESOURCES_MAP));
	}

	public static final String getConfigByKey(final String key) {
		return getConfig().get(key);
	}

	@SuppressWarnings("unchecked")
	public static final Map<String, AppSequence> getApplicationSequenceCache() {
		return (Map<String, AppSequence>) getApplicationCache().getCacheObject(
				SEQ_RULE_MAP);
	}

	public static final ApplicationCache getApplicationCache() {
		return getSpringBean("isperp.applicationCache");
	}

	/**
	 * 刷新Cache
	 * 
	 * @param key
	 *            建
	 * @param value
	 *            值
	 */
	public static final void refreshCache(String key, Object value) {
		// 设置系统常用配置经缓存
		ApplicationCache applicationCache = getApplicationCache();
		if (applicationCache.getCacheObject(key) == null) {
			applicationCache.putCacheObject(key, value);
		} else {
			applicationCache.removeCacheObject(key);
			applicationCache.putCacheObject(key, value);
		}
	}

	/**
	 * 重新构建序列后规则
	 */
	public static final void rebuildSeqRlue() {
		AppSequenceService appSequenceService = getSpringBean("appSequenceService");
		JqGridRequest req = new JqGridRequest();
		req.isPageable(false);

		DataResponse<AppSequence> result = appSequenceService.findAll(req);

		Map<String, AppSequence> map = new HashMap<>();
		for (AppSequence seq : result) {
			map.put(seq.getTableName(), seq);
		}

		refreshCache(SEQ_RULE_MAP, map);

	}

	/**
	 * 重置系统常用配置缓存，所谓常用配置是可以在界面定义的常用数据，如企业名称，邮箱地址等等
	 */
	public static final void rebuildConfigs() {
		// ~-------------此部分可重构---------------- ~//
		AppConfigService appConfigService = getSpringBean("appConfigService");

		List<AppConfig> configs = appConfigService.findAll();

		Map<String, String> maps = new HashMap<String, String>();
		for (AppConfig c : configs) {
			maps.put(c.getItemKey(), c.getItemValue());
		}

		refreshCache(CONFIG_RESOURCES_MAP, maps);

		String wechatCpEnable = maps.get("wechatCpEnable");

		if ("1".equals(wechatCpEnable)) {
			WxCpService wxCpService = ContextHolder
					.getSpringBean("wxCpService");
			wxCpService.setWxCpConfigStorage(getWxCpConfig());

		}

		String wechatMpEnable = maps.get("wechatMpEnable");
		if ("1".equals(wechatMpEnable)) {
			WxMpService wxMpService = ContextHolder
					.getSpringBean("wxMpService");
			wxMpService.setWxMpConfigStorage(getWxMpConfig());

		}

		EmailSender.refreshCache();

		EmailReader.refreshCache();
	}

	/**
	 * 根据给出的beanId来获取在Spring当中配置的bean
	 * 
	 * @param beanId
	 *            给出的beanId
	 * @return 返回找到的bean对象
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getSpringBean(final String beanId) {
		return (T) getSpringBeanFactory().getBean(beanId);
	}

	/**
	 * 根据给出的{@link Class}类型来获取在Spring当中配置的bean
	 * 
	 * @param clazz
	 *            {@link Class}类型
	 * @return
	 */
	public static <T> T getSpringBean(final Class<T> clazz) {
		return getSpringBeanFactory().getBean(clazz);
	}

	/**
	 * 重新载入系统权限资源定义
	 */
	public static final void reloadAuthorityResourceDefinition() {

		IMenuService menuService = getSpringBean("appMenuService");
		Map<String, Collection<ConfigAttribute>> resourceMap = new HashMap<String, Collection<ConfigAttribute>>();

		final List<? extends IMenu> resources = menuService.findAll();

		final List<? extends RoleResource> rrs = menuService
				.findAllRoleResource();

		for (IMenu r : resources) {
			String auth = r.getAuthority();
			if (StringUtils.isNotEmpty(auth) && !resourceMap.containsKey(auth)) {
				Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>();
				atts.add(new SecurityConfig("admin"));// 管理员具有所有权限
				for (RoleResource rs : rrs) {
					if (auth.equals(rs.getAuthority())) {
						atts.add(new SecurityConfig(rs.getRoleId()));
					}
				}
				resourceMap.put(r.getAuthority(), atts);
			}
		}
		getApplicationCache().putCacheObject(MENU_RESOURCES_MAP, resourceMap);
		AuthorityResourceReloade = true;
	}

	private static volatile boolean AuthorityResourceReloade;

	/**
	 * 菜单是否重新载入
	 * 
	 * @return
	 */
	public static final boolean getAuthorityResourceDefinitionReloaded() {
		return AuthorityResourceReloade;
	}

	public static final boolean setAuthorityResourceDefinitionReloaded(boolean b) {
		return AuthorityResourceReloade = b;
	}

	/**
	 * 获取所有权限资源定义
	 * 
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static final Map<String, Collection<ConfigAttribute>> getAuthorityResourceDefinition() {
		return (Map<String, Collection<ConfigAttribute>>) getApplicationCache()
				.getCacheObject(MENU_RESOURCES_MAP);
	}

	// 获取站点路径
	public static String getWebServerName(HttpServletRequest request) {
		String webServerName = getConfigByKey("webServerName");
		if (StringUtils.isBlank(webServerName)) {
			webServerName = request.getScheme() + "://"
					+ request.getServerName() + ":" + request.getServerPort()
					+ request.getContextPath() + "/";
		}
		return webServerName;
	}

	// 获取站点路径
	public static String getWebServerName() {
		return getWebServerName(getRequest());
	}

	/**
	 * 取得WebApplicationContext对象.
	 * <p>
	 * 根据web.xml中对的配置顺序,在Spring启动完成后可用.
	 * </p>
	 * 
	 * @return 返回当前应用Spring的WebApplicationContext对象
	 */
	public static final ApplicationContext getSpringBeanFactory() {
		return webApplicationContext;
	}

	/**
	 * 获取当前线程的HttpServletRequest对象
	 * 
	 * @return 返回当前线程的HttpServletRequest对象
	 */
	public static final HttpServletRequest getRequest() {
		// return requestThreadLocal.get();

		return ((ServletRequestAttributes) RequestContextHolder
				.getRequestAttributes()).getRequest();
	}

	/**
	 * 获取框架上下文
	 * 
	 * @return 框架上下文
	 */
	public static SecurityContext getContext() {
		return SecurityContextHolder.getContext();
	}

	/**
	 * 获取框架上下文
	 * 
	 * @return 框架上下文
	 */
	public static boolean isLogin() {
		if (SecurityContextHolder.getContext().getAuthentication() == null) {
			return false;
		}
		return SecurityContextHolder.getContext().getAuthentication()
				.getPrincipal() instanceof IUser;
	}

	/**
	 * 获取当前登录人员
	 * 
	 * @return 人员实体接口
	 */
	public static IUser getLoginUser() {
		SecurityContext securitycontext = SecurityContextHolder.getContext();
		if (securitycontext != null) {
			Authentication auth = securitycontext.getAuthentication();
			if (auth != null) {
				Object principal = auth.getPrincipal();
				if (principal instanceof IUser)
					return (IUser) principal;
			}
		}
		return null;
	}

	private static Map<String, TempOperator> operators = new HashMap<String, TempOperator>();

	public static void addTempOperator(String relationId, String username,
			String fullname) {
		if (!operators.containsKey(relationId))
			operators.put(relationId, new TempOperator(relationId, username,
					fullname));
	}

	public static TempOperator getTempOperator(String relationId) {
		return operators.get(relationId);
	}

	public static void removeTempOperator(String relationId) {
		operators.remove(relationId);
	}

	public static boolean containsTempOperator(String relationId) {
		return operators.containsKey(relationId);
	}

	/**
	 * 获取当前登录用户的用户名
	 * 
	 * @return 登录用户的用户名
	 */
	public static String getLoginUsername() {
		return SecurityContextHolder.getContext().getAuthentication().getName();
	}

	/**
	 * 得到当前线程中的HttpServletResponse对象
	 * 
	 * @return 返回当前线程的HttpServletResponse对象
	 */
	public static HttpServletResponse getResponse() {
		return responseThreadLocal.get();
	}

	/**
	 * 将一个key-value对放到当前线程Request中的Attribute当中
	 * 
	 * @param key
	 *            key值
	 * @param obj
	 *            具体对象
	 */
	public static void setRequestAttribute(final String key, final Object obj) {
		requestThreadLocal.get().setAttribute(key, obj);
	}

	/**
	 * 从当前线程中Request中取Attribute值
	 * 
	 * @param key
	 *            Attribute值对应的key
	 * @return 返回与该key对应的值对象
	 */
	public static Object getRequestAttribute(final String key) {
		return requestThreadLocal.get().getAttribute(key);
	}

	/**
	 * 从当前线程中Request中取parameter值
	 * 
	 * @param key
	 *            parameter的key
	 * @return 与key对象的字符串值
	 */
	public static String getRequestParameter(final String key) {
		return requestThreadLocal.get().getParameter(key);
	}

	/**
	 * 清除上下文信息. 包括:
	 * <p>
	 * 1.框架上下文；2.当前线程http响应；3.当前线程http请求；4.当前线程模块；5.当前的数据源；
	 * </p>
	 */
	public static void clearContext() {

		responseThreadLocal.remove();
		requestThreadLocal.remove();
	}

	/**
	 * 设置当前线程的HttpServletRequest对象及当前线程的HttpServletResponse对象
	 * 
	 * @param requestResponseHolder
	 */
	public static void setHttpRequestResponseHolder(
			HttpRequestResponseHolder requestResponseHolder) {
		responseThreadLocal.set(requestResponseHolder.getResponse());
		requestThreadLocal.set(requestResponseHolder.getRequest());
	}

}
